#include "NetIO.h"

Operation::PERFORM_FUNC
Operation::op_func_[ OperationType::MAX ] =
{
	&Operation::read,
	&Operation::write,
	&Operation::connect,
	&Operation::accept
};

bool Operation::perform( )
{
	if( optype_ <= OperationType::NONE ||
	        optype_ >= OperationType::MAX )
		return false;

	PERFORM_FUNC func = op_func_[ optype_ ];
	return ( this->*func )( );
}

bool Operation::read( )
{
	int readlen;

#ifdef MODE_SPECULATIVE
	int this_readlen = 0;
#endif

	while( 1 )
	{
		int space = ( int ) data_block_->space( );
		char *buf = data_block_->wr_ptr( );

		if( space <= 0 )
			break;

		readlen = recv( handle_, buf, space, 0 );

		if( readlen < 0 )
		{
#ifdef WIN32
			errcode_ = WSAGetLastError( );
#else
			errcode_ = errno;
#endif

			break;
		}
		else if( readlen == 0 )
		{
			errcode_ = -1;
			break;
		}

		bytes_transferred_ += readlen;
		data_block_->wr_ptr( readlen );

#ifdef MODE_SPECULATIVE
		this_readlen += readlen;
#endif
	}

#ifndef WIN32

	if( errcode_ == EAGAIN )
#else
	if( errcode_ == WSAEWOULDBLOCK )
#endif
		errcode_ = 0;

#ifdef MODE_SPECULATIVE

	if( errcode_ == 0 && this_readlen == 0 && data_block_->space( ) > 0 )
		return false;

#endif

	return true;
}

bool Operation::write( )
{
	int writelen;

	while( 1 )
	{
		int length = ( int ) data_block_->length( );
		char *buf = data_block_->rd_ptr( );

		if( length <= 0 )
			break;

		writelen = send( handle_, buf, length, 0 );

		if( writelen <= 0 )
		{
#ifdef WIN32
			errcode_ = WSAGetLastError( );
#else
			errcode_ = errno;
#endif
			break;
		}

		bytes_transferred_ += writelen;
		data_block_->rd_ptr( writelen );
	}

	if(
#ifndef WIN32
	    errcode_ == EAGAIN &&

#else
	    errcode_ == WSAEWOULDBLOCK &&
#endif
	    data_block_->length( ) > 0 )
	{
		errcode_ = 0;
		return false;
	}

	return true;
}

bool Operation::connect( )
{
	int connect_error = 0;
	socklen_t connect_error_len = sizeof( connect_error );

	if( getsockopt( handle_, SOL_SOCKET, SO_ERROR,
	                ( char* ) &connect_error, &connect_error_len ) == -1 )
	{
#ifndef WIN32
		errcode_ = errno;
#else
		errcode_ = WSAGetLastError( );
#endif
		return true;
	}

	if( connect_error )
	{
		errcode_ = connect_error;
		return true;
	}

	return true;
}

bool Operation::accept( )
{
	sockaddr_in addr;
	socklen_t len = sizeof( addr );
	handle_( ) = ::accept( handle_, ( sockaddr* ) &addr, &len );
	if( handle_ == INVALID_HANDLE )
	{
#ifdef MODE_SPECULATIVE
		return false;
#else

#ifndef WIN32
		errcode_ = errno;
#else
		errcode_ = WSAGetLastError( );
#endif
		return true;
#endif
	}

//	ip_.assign (inet_ntoa (addr.sin_addr));
//	port_ = ntohs (addr.sin_port);

	SockOps::set_non_blocking( handle_ );
	reactor_->register_handler( handle_ );
	return true;
}
